﻿using ArduinoAnalyzer.LexicalAnalysis;
using ArduinoAnalyzer.SemanticAnalysis;
using ArduinoAnalyzer.SyntacticAnalysis;
using ArduinoAnalyzer.Tokens;
using ArduinoAnalyzer.Tokens.LexicalAnalysis;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ArduinoAnalyzer
{
    public partial class MainForm : Form
    {
        #region Constructors

        public MainForm()
        {
            InitializeComponent();
        }

        #endregion

        #region Private Methods

        private void GetCodeCurrentLineColumn(int position, out int line, out int column)
        {
            line = rtbCode.GetLineFromCharIndex(position);

            int firstChar = rtbCode.GetFirstCharIndexFromLine(line);
            column = position - firstChar;
        }

        private int GetCodeCurrentLineColumn(int line, int column)
        {
            int firstChar = rtbCode.GetFirstCharIndexFromLine(line);
            return firstChar + column;
        }

        private string FormatErrorMessage(LexicalErrorException lex)
        {
            int line;
            int column;
            this.GetCodeCurrentLineColumn(lex.Position, out line, out column);
            return string.Format("(Erro em: Linha: {0} Coluna: {1})", line, column);
        }

        private void ColorTokens(IEnumerable<Token> tokens)
        {
            foreach (var token in tokens)
            {
                var oldStart = this.rtbCode.SelectionStart;
                var oldLength = this.rtbCode.SelectionLength;

                Color color;
                if (token.Type == TokenType.Comment)
                    color = Color.Green;
                else if (token.Type == TokenType.Void ||
                    token.Type == TokenType.Byte ||
                    token.Type == TokenType.Int ||
                    token.Type == TokenType.Unsigned ||
                    token.Type == TokenType.Word ||
                    token.Type == TokenType.Short ||
                    token.Type == TokenType.Long ||
                    token.Type == TokenType.Float ||
                    token.Type == TokenType.Double ||
                    token.Type == TokenType.Boolean ||
                    token.Type == TokenType.Break ||
                    token.Type == TokenType.Return ||
                    token.Type == TokenType.Continue ||
                    token.Type == TokenType.GoTo ||
                    token.Type == TokenType.Static ||
                    token.Type == TokenType.Volatile ||
                    token.Type == TokenType.Const ||
                    token.Type == TokenType.For ||
                    token.Type == TokenType.If ||
                    token.Type == TokenType.Else ||
                    token.Type == TokenType.Do ||
                    token.Type == TokenType.While ||
                    token.Type == TokenType.Switch ||
                    token.Type == TokenType.Case ||
                    token.Type == TokenType.Default ||
                    token.Type == TokenType.True ||
                    token.Type == TokenType.False)
                    color = Color.Blue;
                else
                    color = this.rtbCode.ForeColor;

                this.rtbCode.Select(token.Position, token.Lexeme.Length);
                this.rtbCode.SelectionColor = color;

                this.rtbCode.Select(oldStart, oldLength);
            }
        }

        #endregion

        #region Signed Event Methods

        private void rtbCode_TextChanged(object sender, EventArgs e)
        {
            this.lbTokenList.DataSource = null;
            var list = new List<Token>();
            var analyzer = new LexicalAnalyzer();
            var text = rtbCode.Text;

            try
            {
                foreach (var token in analyzer.GetTokens(text))
                    list.Add(token);

                this.lblLexicalAnalysisStatus.Text = string.Empty;
                this.lblLexicalAnalysisStatus.Image = Properties.Resources.Success;
            }
            catch (LexicalErrorException lex)
            {
                this.lblLexicalAnalysisStatus.Text = this.FormatErrorMessage(lex);
                this.lblLexicalAnalysisStatus.Image = Properties.Resources.Error;
            }
            catch (Exception ex)
            {
                this.lblLexicalAnalysisStatus.Text = string.Format("({0})", ex.Message);
                this.lblLexicalAnalysisStatus.Image = Properties.Resources.Error;
            }

            this.lbTokenList.DataSource = list;

            //this.ColorTokens(list);

            analyzer = new LexicalAnalyzer();
            var syntacticAnalyzer = new SyntacticAnalyzer(analyzer.GetTokens(text).GetEnumerator());
            Code code;
            this.lblSyntacticAnalysisStatus.Image = syntacticAnalyzer.Validate(out code) ? Properties.Resources.Success : Properties.Resources.Error;
            this.lblSemanticAnalysisStatus.Image = syntacticAnalyzer.SemanticErrors.Any() ? Properties.Resources.Error : Properties.Resources.Success;
            this.lbSemanticErrors.DataSource = syntacticAnalyzer.SemanticErrors;

            this.rtbCompiledCode.Text = code.ToString();
        }

        private void rtbCode_SelectionChanged(object sender, EventArgs e)
        {
            int line;
            int column;

            this.GetCodeCurrentLineColumn(rtbCode.SelectionStart, out line, out column);

            this.lblPosition.Text = string.Format("Linha: {0} Coluna: {1}", line, column);
        }

        private void lbTokens_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var itemIndex = this.lbTokenList.IndexFromPoint(e.Location);

            if (itemIndex < 0)
                return;

            var token = this.lbTokenList.Items[itemIndex] as Token;
            if (token == null)
                return;

            var position = this.GetCodeCurrentLineColumn(token.Line, token.Column);
            this.rtbCode.Select(position, token.Lexeme.Length);
            this.rtbCode.Focus();
        }

        private void lbSemanticErrors_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            var itemIndex = this.lbSemanticErrors.IndexFromPoint(e.Location);

            if (itemIndex < 0)
                return;

            var semanticError = this.lbSemanticErrors.Items[itemIndex] as SemanticError;
            if (semanticError == null || semanticError.Token == null)
                return;

            var position = this.GetCodeCurrentLineColumn(semanticError.Token.Line, semanticError.Token.Column);
            this.rtbCode.Select(position, semanticError.Token.Lexeme.Length);
            this.rtbCode.Focus();
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            var dialog = new OpenFileDialog()
            {
                Multiselect = false
            };

            if (dialog.ShowDialog() != DialogResult.OK)
                return;

            var reader = new StreamReader(dialog.FileName);
            string text = reader.ReadToEnd();

            this.rtbCode.Text = text;
        }

        private void btnExportReport_Click(object sender, EventArgs e)
        {
            try
            {
                var analyzer = new LexicalAnalyzer();
                var report = new TokensReport(analyzer.GetTokens(this.rtbCode.Text).ToList());

                var dialog = new SaveFileDialog
                {
                    Filter = "Arquivo de texto|.txt",
                    DefaultExt = ".txt",
                    AddExtension = true
                };

                if (dialog.ShowDialog() != DialogResult.OK)
                    return;

                using (var writer = new StreamWriter(dialog.FileName))
                    writer.Write(report.GetReport());

                if (File.Exists(dialog.FileName))
                    Process.Start(dialog.FileName);
            }
            catch (LexicalErrorException lex)
            {
                MessageBox.Show(this.FormatErrorMessage(lex), "Erro léxico", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #endregion
    }

    public class A
    {
        void setup(int a, int b)
        {
            return;
        }

        int update()
        {
            setup(1, 2);
            return 0;
        }

        int B, F, code;
        void loop()
        {
            int i = 0;
            int val = 0;
            int checksum = 0;
            int bytesread = 0;
            int tempbyte = 0;
            int available = 0;

            if (available > 0)
            {
                if (val == 2)
                {
                    bytesread = 0;
                    while (bytesread < 12)
                    {
                        if (available > 0)
                        {
                            val = 34;

                            if ((val >= 0) && (val <= 9))
                            {
                                val = val - 0;
                            }
                            else if ((val >= B) && (val <= F))
                            {
                                val = 10 + val;
                            }

                            if (true/*bytesread & 1 == 1*/)
                            {
                                //bytesread >> 1 == (val | (tempbyte << 4));

                                if (bytesread >> 1 != 5) // If were at the checksum byte,
                                {
                                    checksum ^= bytesread >> 1; // Calculate the checksum... (XOR)
                                };
                            }
                            else
                            {
                                tempbyte = val; // Store the first hex digit first...
                            };

                            bytesread++; // ready to read next digit
                        }
                    }

                    // Output to Serial:

                    if (bytesread == 12) // if 12 digit read is complete
                    {

                        for (i = 0; i < 5; i++)
                        {
                            if (code < 16)
                            {
                                int ggg = 1;
                            }
                        }
                    }

                    bytesread = 0;
                }
            }
        }
    }
}
